﻿/**
 * 8051虚拟机具体实现
 *
 * 创建时间：2022年5月21日
 * 作者：李述铜
 * 联系邮箱: 527676163@qq.com
 */
#include <string.h>
#include <stdio.h>
#include "simu8051.h"

// 8051 simulator
static simu8051_t simu;

/**
 * 读取位寻址区，包含0
 */
static uint8_t read_bit (uint8_t addr) {
    uint8_t bit_idx = addr % 8;

    if (addr < MEM_BIT_SFR_START) {
        uint8_t byte_idx = addr / 8 + MEM_BIT_IRAM_START;
        return simu.mem.iram[byte_idx] & ( 1 << bit_idx) ? 1 : 0;
    } else {
        uint8_t byte_idx = (addr - MEM_BIT_SFR_START) / 8 * 8;
        return simu.mem.sfr[byte_idx] & (1 << bit_idx) ? 1 : 0;
    }
}

static void write_bit (uint8_t addr, int bit) {
    uint8_t bit_idx = addr % 8;

    bit &= 0x1;
    if (addr < MEM_BIT_SFR_START) {
        uint8_t byte_idx = addr / 8 + MEM_BIT_IRAM_START;
        simu.mem.iram[byte_idx] &= ~(1 << bit_idx);
        simu.mem.iram[byte_idx] |= bit ? (1 << bit_idx) : 0;
    } else {
        uint8_t byte_idx = (addr - MEM_BIT_SFR_START) / 8 * 8;
        simu.mem.sfr[byte_idx] &= ~(1 << bit_idx);
        simu.mem.sfr[byte_idx] |= bit ? (1 << bit_idx) : 0;
    }
}

static uint8_t read_sfr(uint8_t addr) {
    switch (addr) {
        default: {
            addr -= MEM_BIT_SFR_START;
            return (addr < MEM_SFR_SIZE) ? simu.mem.sfr[addr] : 0;
        }
    }
}

static void write_sfr(uint8_t addr, uint8_t data) {
    switch (addr) {
        default: {
            addr -= MEM_BIT_SFR_START;
            if (addr < MEM_SFR_SIZE) {
                simu.mem.sfr[addr] = data;
            }
            break;
        }
    }
}

/**
 * read internal ram
 */
static uint8_t read_iram (uint8_t addr) {
    uint8_t data;

    if (addr >= MEM_IRAM_SIZE) {
        data = read_sfr(addr);
    } else {
        data = simu.mem.iram[addr];
    }

    return data;
}

/**
 * write internal ram
 */
static void write_iram (uint8_t addr, uint8_t data) {
    if (addr >= MEM_IRAM_SIZE) {
        write_sfr(addr, data);
    } else {
        simu.mem.iram[addr] = data;
    }
}

/**
 * read external ram
 */
static uint8_t read_xram (uint16_t addr) {
    return simu.mem.xram[addr];
}

/**
 * write external ram
 */
static void write_xram (uint16_t addr, uint8_t data) {
    simu.mem.xram[addr] = data;
}

/**
 * read code memory
 */
static uint8_t read_code (uint16_t addr) {
    return simu.mem.code[addr];
}

/**
 * write code ram
 */
static void write_code(uint16_t addr, uint8_t data) {
    simu.mem.code[addr] = data;
}

uint8_t simu8051_read (mem_type_t type, uint16_t addr) {
    switch (type) {
    case MEM_TYPE_IRAM:
        return read_iram((uint8_t)addr);
    case MEM_TYPE_XRAM:
        return read_xram(addr);
    case MEM_TYPE_CODE:
        return read_code(addr);
    case MEM_TYPE_SFR:
        return read_sfr((uint8_t)addr);
    case MEM_TYPE_BIT:
        return read_bit((uint8_t)addr);
    default:
        return 0;
    }
}

void simu8051_write (mem_type_t type, uint16_t addr, uint8_t data) {
    switch (type) {
    case MEM_TYPE_IRAM:
        write_iram((uint8_t)addr, data);
        break;
    case MEM_TYPE_XRAM:
        write_xram(addr, data);
        break;
    case MEM_TYPE_CODE:
        write_code(addr, data);
        break;
    case MEM_TYPE_SFR:
        write_sfr((uint8_t)addr, data);
        break;
    case MEM_TYPE_BIT:
        write_bit((uint8_t) addr, data);
        break;
    }
}

void simu8051_reset(void) {
    simu.pc = 0x0;
    simu.cycles = 0;
    write_sfr(SFR_SP, 0x7);
}

/**
 * initialize 8051 simulator
 */
void simu8051_init(void) {
    simu.mem.code = (uint8_t *)0;
    memset(simu.mem.xram, 0, MEM_XRAM_SIZE);
    memset(simu.mem.iram, 0, MEM_IRAM_SIZE);
    memset(simu.mem.sfr, 0, MEM_SFR_SIZE);

    simu8051_reset();
}

uint16_t simu8051_pc(void) {
    return simu.pc;
}

uint32_t simu8051_cycle(void) {
    return simu.cycles;
}

void simu8051_fetch_instr(instr_t * instr) {
    instr->opcode = simu.mem.code[simu.pc];
    instr->op0 = simu.mem.code[simu.pc + 1];
    instr->op1 = simu.mem.code[simu.pc + 2];
}

void simu8051_exec (instr_t * instr) {
    switch (instr->opcode) {
        case 0x00: {
            printf("%d: %s\n", simu.pc, "nop");
            simu.pc += 1;
            simu.cycles += 1;
            break;
        }
        case 0x80: {
            printf("%d: %s\n", simu.pc, "sjmp");
            simu.pc += 2 + (int8_t)instr->op0;
            simu.cycles += 2;
            break;
        }
    }
}

void simu8051_load(uint8_t * code) {
    simu.mem.code = code;
}
